#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/err.h>
#include <asm/types.h>

#include <linux/fs.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>

#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/unistd.h>
#include <linux/syscalls.h>
#include <linux/mm.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/sched.h>
#include <linux/slab.h>
// #include <signal.h>
// #include <time.h>
#include <linux/string.h>
#include <linux/ctype.h>

#define MINORS 2
static int ft_wdt_major;

static struct class *ft_wdt_drv_class;

#define WDT_WRR 0x0000
#define WDT_W_IIR 0x0FCC
#define WDT_WCS 0x1000
#define WDT_WOR 0x1008
#define WDT_WCVL 0x1010
#define WDT_WCVH 0x1014

volatile unsigned int *wdt0_wcs;
volatile unsigned int *wdt0_wrr;
volatile unsigned int *wdt0_wor;
volatile unsigned int *wdt0_wcvl;
volatile unsigned int *wdt0_wcvh;

#define WDT0_BASE 0x0002800A000
#define ONE_SECOND 24 * 1000 * 1000 // 48M Hz

// static int en=1;
static int wdt_init_timeout = 30;
#define WDT_MAX_TIMEOUT 60 // 定时器设置的实际最高值为179秒，为计算方便，使用60秒

static int log = 0;

// module_param(en,int,S_IRUSR);
// module_param(wdt_init_timeout,int,S_IRUSR);
// module_param(log,int,S_IRUSR);

typedef enum
{
    Wdt_Init = 0,
    Wdt_Start,
    Wdt_Stop,
    Wdt_Feed,
} WdtState_t;

struct Wdt_Setting_t
{
    int minutes;
    int seconds;
    int time_out;         // 设置的最大不重启时间
    int self_flush_count; // 自刷新次数，用于大于WDT_MAX_TIMEOUT情况
};

WdtState_t WdtState = Wdt_Init;
struct Wdt_Setting_t Wdt_Setting;

#define print_log(fmt, args...)            \
    do                                     \
    {                                      \
        if (log == 1)                      \
            printk("kernel " fmt, ##args); \
    } while (0)

struct timer_list wdt_timer;
// static struct class *wdt_drv_class;
// static struct class_device *wdt_drv_dev;
int timer_counter = 0;
int timer_offset = 10;

void Feed_Dog(void)
{
    *wdt0_wrr = 0x1; // feed dog reg
}
void Wdt_Enable(unsigned char Enable)
{
    if (Enable == 1)
    {
        *wdt0_wcs |= (0x1 << 0); // wdt en
    }
    else
    {
        *wdt0_wcs &= ~(0x1 << 0); // wdt disable
    }
}
void Wdt_Set_Timeout(int Seconds)
{
    *wdt0_wor = Seconds * ONE_SECOND;
    print_log("%s %d\r\n", __func__, Seconds);
}

static void count_dog(struct timer_list *timer)
{

    mod_timer(&wdt_timer, jiffies + msecs_to_jiffies(1000)); // 1S

    if (Wdt_Setting.self_flush_count > 0) // 用于超过60秒的情况
    {
        if (timer_counter == (WDT_MAX_TIMEOUT - timer_offset))
        {
            timer_counter = 0;
            Feed_Dog();
            Wdt_Setting.self_flush_count--;
            print_log("self flush, remain %d times\r\n", Wdt_Setting.self_flush_count);

            if (Wdt_Setting.self_flush_count == 0)
            {
                Wdt_Set_Timeout(Wdt_Setting.time_out);
            }
        }
    }

    timer_counter++;

    if (Wdt_Setting.time_out > 0)
        Wdt_Setting.time_out--;

    //	print_log("timer_counter: %d,Wdt_Setting.time_out=%d\r\n",timer_counter,Wdt_Setting.time_out);
}

static int ft_wdt_drv_open(struct inode *inode, struct file *file)
{

    //	print_log("ft_wdt_drv_open!\n");

    return 0;
}

static ssize_t ft_wdt_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    int status[1] = {0};

    //	printk("wdtstate=%d\r\n",WdtState);
    if ((WdtState == Wdt_Start) || (WdtState == Wdt_Feed))
    {
        status[0] = Wdt_Setting.time_out;
    }
    else
    {
        status[0] = wdt_init_timeout;
    }

    //	print_log("status[0]=%d\r\n",status[0]);
    copy_to_user(buf, status, sizeof(status));

    return 1;
}
int Check_Buf(char input_buf[])
{
    char *feed_pos, *space_pos, *number_str, *end;
    long number;

    // 查找 "feed" 的位置
    feed_pos = strstr(input_buf, "feed");
    if (feed_pos == NULL)
    {
        print_log("No 'feed' found in the string.\n");
        return 0; // 模块初始化成功返回 0
    }

    // 查找 "feed" 后面的空格
    space_pos = strchr(feed_pos, ' ');
    if (space_pos == NULL || space_pos == feed_pos)
    {
        // 如果没有找到空格或者空格紧跟着 "feed"，则没有数字
        print_log("No space found after 'feed'.\n");
        return 0;
    }

    // 判断空格后是否是数字
    if (isdigit(*(space_pos + 1)))
    {
        // 提取数字字符串
        number_str = space_pos + 1;

        // 查找数字字符串的结尾
        end = number_str;
        while (*end && isdigit(*end))
        {
            end++;
        }

        // 确保我们找到了数字
        if (end == number_str)
        {
            print_log("No digits found after the space.\n");
            return 0;
        }

        // 将数字字符串转换为长整数，注意这里我们不需要提供基数，因为默认是 10（十进制）
        // 但在实际使用中，最好明确提供基数，如 simple_strtol(number_str, &end, 10)
        number = simple_strtol(number_str, &end, 10);

        // 检查转换是否成功（即 end 是否指向了数字字符串的末尾）
        if (*end != '\0')
        {
            print_log("Failed to convert number string.\n");
            return 0;
        }

        // 打印结果
        print_log("Extracted number: %ld\n", number);
    }
    else
    {
        print_log("No digits found after the space.\n");
    }

    return number;
}
static ssize_t ft_wdt_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos)
{
    char dog_buf[100] = {0};
    int number = 0;

    copy_from_user(dog_buf, buf, size);

    print_log("%s,dog_buf=%s,size=%ld\r\n", __func__, dog_buf, size);

    number = Check_Buf(dog_buf); // 适用于feed + num情况
    timer_counter = 0;

    if (number != 0) // feed + num
    {

        Wdt_Enable(0); // wdt disable
        Wdt_Setting.self_flush_count = 0;
        print_log("number=%d,WDT_MAX_TIMEOUT=%d\r\n", number, WDT_MAX_TIMEOUT);
        if (number >= WDT_MAX_TIMEOUT)
        {
            Wdt_Set_Timeout(WDT_MAX_TIMEOUT); // set max count
            Wdt_Setting.self_flush_count = number / WDT_MAX_TIMEOUT;
            print_log("Wdt_Setting.self_flush_count=%d\r\n", Wdt_Setting.self_flush_count);
        }

        Wdt_Setting.time_out = number; // timer count

        Feed_Dog();
        print_log("feed dog %d seconds\r\n", number);
        Wdt_Enable(1); // wdt en
        WdtState = Wdt_Feed;
    }
    else
    {
        if (strcmp(dog_buf, "start") == 0)
        {

            print_log("set to default 30 seconds\r\n");
            Wdt_Set_Timeout(wdt_init_timeout);
            Wdt_Setting.time_out = wdt_init_timeout; // total timeout
            Wdt_Enable(1);                           // wdt en
            WdtState = Wdt_Start;

            print_log("start dog\r\n");
        }
        else if (strcmp(dog_buf, "stop") == 0)
        {
            Wdt_Enable(0); // wdt disable
            WdtState = Wdt_Stop;

            print_log("stop dog\r\n");
        }
        /*	else if(strcmp(dog_buf,"feed")==0)
            {
                Feed_Dog();
                WdtState=Wdt_Feed;

                print_log("feed dog\r\n");
            }*/
        else
        {
            WdtState = Wdt_Init;
            // print_log("not feed dog\r\n");
        }
    }

    return size;
}
static int ft_wdt_drv_close(struct inode *inode, struct file *file)
{
    return 0;
}

static int ft_wdt_drv_mmap(struct file *file, struct vm_area_struct *vma)
{
    int ret;

    vma->vm_flags |= VM_IO;
    //	vma->vm_flags |= VM_RESERVED;
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

    ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, vma->vm_end - vma->vm_start,
                          vma->vm_page_prot);
    if (ret)
    {
        print_log("remap_pfn_range err!\n");
    }

    print_log("pgoff = %lx,start= %lx, end= %lx\n", vma->vm_pgoff, vma->vm_start, vma->vm_end);
    return 0;
}

static struct file_operations ft_wdt_drv_fops = {
    .owner = THIS_MODULE, /* 这是一个宏，推向编译模块时自动创建的__this_module变量 */
    .open = ft_wdt_drv_open,
    .read = ft_wdt_drv_read,
    .write = ft_wdt_drv_write,

    .release = ft_wdt_drv_close,
    .mmap = ft_wdt_drv_mmap,
};

static struct cdev ft_wdt_cdev;
int major;
static int ft_wdt_drv_init(void)
{
    int result;
    dev_t devno = MKDEV(ft_wdt_major, 0);

    print_log("%s\r\n", __func__);
    if (ft_wdt_major)
    {
        result = register_chrdev_region(devno, MINORS, "ft_wdt_drv");
    }
    else
    {

        result = alloc_chrdev_region(&devno, 0, MINORS, "ft_wdt_drv");
        ft_wdt_major = MAJOR(devno);
    }

    if (result < 0)
    {
        print_log("alloc_chrdev_region err\n");
        return result;
    }

    cdev_init(&ft_wdt_cdev, &ft_wdt_drv_fops);
    cdev_add(&ft_wdt_cdev, devno, MINORS);
    ft_wdt_drv_class = class_create(THIS_MODULE, "ft_wdt_drv");
    device_create(ft_wdt_drv_class, NULL, MKDEV(ft_wdt_major, 0), NULL, "ft_wdt"); /* /dev/ft_wdt */

    wdt0_wcs = (volatile unsigned int *)ioremap(WDT0_BASE + WDT_WCS, 16);
    wdt0_wrr = (volatile unsigned int *)ioremap(WDT0_BASE + WDT_WRR, 16);
    wdt0_wor = (volatile unsigned int *)ioremap(WDT0_BASE + WDT_WOR, 16);
    wdt0_wcvh = (volatile unsigned int *)ioremap(WDT0_BASE + WDT_WCVH, 16);
    wdt0_wcvl = (volatile unsigned int *)ioremap(WDT0_BASE + WDT_WCVL, 16);

    if (wdt_init_timeout < 179) // 24*1000*1000*179=0xFEA1 8C00, almost 0xFFFF FFFF
        Wdt_Set_Timeout(wdt_init_timeout);
    else
    {
        print_log("set to default 30 seconds\r\n");
        Wdt_Set_Timeout(30);
    }
    //	*wdt0_wcvh = 0;
    //	*wdt0_wcvl = 0;
    print_log("wdt0_wor=0x%x\r\n", *wdt0_wor);

#if 0
	init_timer(&wdt_timer);//kylin
	//init_timers(&wdt_timer);//uos
	wdt_timer.expires = jiffies+msecs_to_jiffies(1000);
	wdt_timer.data=250;
	wdt_timer.function=count_dog;
	add_timer(&wdt_timer);
#endif

#if 1
    wdt_timer.expires = jiffies + msecs_to_jiffies(1000);
    timer_setup(&wdt_timer, count_dog, 0);
    add_timer(&wdt_timer);
#endif

    WdtState = Wdt_Init;

    return 0;
}

static void ft_wdt_drv_exit(void)
{
    print_log("%s\r\n", __func__);
    Wdt_Enable(0); // wdt disable
    // del_timer(&wdt_timer);
    del_timer_sync(&wdt_timer);

    device_destroy(ft_wdt_drv_class, MKDEV(ft_wdt_major, 0));
    class_destroy(ft_wdt_drv_class);
    cdev_del(&ft_wdt_cdev);
    unregister_chrdev_region(MKDEV(ft_wdt_major, 0), MINORS);

    iounmap(wdt0_wcs);
    iounmap(wdt0_wrr);
    iounmap(wdt0_wor);
    iounmap(wdt0_wcvh);
    iounmap(wdt0_wcvl);
}

module_init(ft_wdt_drv_init);
module_exit(ft_wdt_drv_exit);

/* 描述驱动程序的一些信息，不是必须的 */
MODULE_AUTHOR("nemon");
MODULE_VERSION("0.1.0");
MODULE_DESCRIPTION("ft2000/4 ft_wdt");
MODULE_LICENSE("GPL");
